#include <iostream>
#include <vector>
#include <sstream>
#include <fstream>
#include "serv_maps.h"

using namespace std;

// Load the map data
void Maps::LoadMap(int mapID)
{
    m_numberOfTeleportation = 0;

    ostringstream t_stream;
    t_stream << "../../data/maps/" << mapID << ".txt";
    string t_string = t_stream.str();

    // Open and read the map file
    ifstream f_map(t_string.c_str(), ios::in);

    if(f_map)
    {
		string t_temp;
        f_map >> m_mapName >> t_temp >> m_mapTileset >> m_mapSizeX >> m_mapSizeY;

        // Resize the vectors
        m_vectLayer1.resize(m_mapSizeX,vector<int>(m_mapSizeY,0));
        /*m_vectLayer2.resize(m_mapSizeX,vector<int>(m_mapSizeY,0));
        m_vectLayer3.resize(m_mapSizeX,vector<int>(m_mapSizeY,0));
        m_vectLayer4.resize(m_mapSizeX,vector<int>(m_mapSizeY,0));*/

        // Put the 3 layers into their vectors
        for(int j=0; j<m_mapSizeY; j++)
        {
            for(int i=0; i<m_mapSizeX; i++)
            {
                int tmp;
                f_map >> tmp;
                m_vectLayer1[i][j] = tmp;
            }
        }

        /*for(int j=0; j<m_mapSizeY; j++)
        {
            for(int i=0; i<m_mapSizeX; i++)
            {
                int tmp;
                f_map >> tmp;
                m_vectLayer2[i][j] = tmp;
            }
        }

        for(int j=0; j<m_mapSizeY; j++)
        {
            for(int i=0; i<m_mapSizeX; i++)
            {
                int tmp;
                f_map >> tmp;
                m_vectLayer3[i][j] = tmp;
            }
        }

        for(int j=0; j<m_mapSizeY; j++)
        {
            for(int i=0; i<m_mapSizeX; i++)
            {
                int tmp;
                f_map >> tmp;
                m_vectLayer4[i][j] = tmp;
            }
        }*/

        f_map.close();
    } else {

		cout << endl;
		cout << "Failed to load " << mapID << ".txt! ";
	}

    // Open and read the tileset configuration file
    ostringstream t_stream2;
    t_stream2 << "../../data/tiles/" << m_mapTileset << ".txt";
    string t_string2 = t_stream2.str();

    ifstream f_tileset(t_string2.c_str(), ios::in);

    if(f_tileset)
    {
        int t_numberOfTiles;
        f_tileset >> t_numberOfTiles;

        m_vectLayerPassability.resize(t_numberOfTiles);
        for (int i=0; i<t_numberOfTiles; i++)
        {
            int tmp;
            f_tileset >> tmp;
            m_vectLayerPassability[i] = tmp;
        }

        f_tileset.close();
    } else {

		cout << "Cannot open '" << t_stream2.str() << "'" << endl;
	}
	
    // Open and read the teleportation file
    ostringstream t_stream3;
    t_stream3 << "../../data/events/" << mapID << "t.txt";
    string t_string3 = t_stream3.str();

    ifstream f_teleportation(t_string3.c_str(), ios::in);

    if(f_teleportation)
    {
        f_teleportation >> m_numberOfTeleportation;

        m_vectTeleportation.resize(5,vector<int>(m_numberOfTeleportation,0));
        for(int j=0; j<m_numberOfTeleportation; j++)
        {
            for(int i=0; i<5; i++)
            {
                int tmp;
                f_teleportation >> tmp;
                m_vectTeleportation[i][j] = tmp;
            }
        }

        f_teleportation.close();
    }
}

// Find the path, return if it's possible or not
bool Maps::FindPath(int startX, int startY, int targetX, int targetY, std::vector<Node> &path)
{
    // Clear vectors
    path.clear();
	vector< vector< int > > whichList(m_mapSizeX, vector<int>(m_mapSizeY, 0));
	vector<Node> closedList;
    vector<Node> openList;

    // Algorithm
    bool pathFound = false;
    int pathStep = 0;
    bool searching = true;
    bool doOnce = true;
    int currentx = startX, currenty = startY;
    int xvalue[] = {+1, +1, +1, 0, -1, -1, -1, 0};
    int yvalue[] = {-1, 0, +1, +1, +1, 0, -1, -1};
    int gvalue[] = {32, 36, 64, 36, 32, 36, 64, 36};
	int dvalue1[] = {8, 0, 2, 0, 4, 0, 6, 0};
	int dvalue2[] = {2, 0, 4, 0, 6, 0, 8, 0};

    while(searching)
    {
        if (doOnce)
        {
            // 1. Check if target is the same as starting point
            if (targetX == startX && targetY == startY)
                return false;

            // 2. Check if target is walkable
            if (m_vectLayerPassability[m_vectLayer1[targetX][targetY]] == 1)
                return false;

			if (targetX < 0 || targetY < 0)
				return false;

			if (targetX > m_mapSizeX-1 || targetY > m_mapSizeY-1)
				return false;

            doOnce = false;
        }

        // 3. Put current step in closed list, remove of open list
        // If the current step is the starting point :
        if (currentx == startX && currenty == startY)
        {
            Node currentNode = {currentx, currenty, 0, -1, 0};
            closedList.push_back(currentNode);
            whichList[currentx][currenty] = 2;

        // ... else :
        } else {

            for (int i=0; i<openList.size(); i++)
            {
                // Find the node in the open list
                if (openList[i].x == currentx && openList[i].y == currenty)
                {
                    // 3.1 If the open list is empty, there is no path
                    if (openList.size() == 0) searching = false;

                    Node currentNode = openList[i];
                    closedList.push_back(currentNode);
                    openList.erase(openList.begin()+i);
                    whichList[currentx][currenty] = 2;

                    // 3.2 If the path is found
                    if (closedList.back().x == targetX && closedList.back().y == targetY)
                    {
                        pathFound = true;
                        searching = false;
                    }
                }
            }
        }

        // 4. Calculate the f of the tiles around, put the in open list if not already
        for (int i=0; i<8; i++)
        {
            if (whichList[currentx+xvalue[i]][currenty+yvalue[i]] == 0 && m_vectLayerPassability[m_vectLayer1[currentx+xvalue[i]][currenty+yvalue[i]]] == 0)
            {
				bool ignoreNode = false;
				if (dvalue1[i] != 0)
					if (m_vectLayerPassability[m_vectLayer1[currentx+xvalue[dvalue1[i]-1]][currenty+yvalue[dvalue1[i]-1]]] == 1 
						|| m_vectLayerPassability[m_vectLayer1[currentx+xvalue[dvalue2[i]-1]][currenty+yvalue[dvalue2[i]-1]]] == 1)
						ignoreNode = true;

				if (!ignoreNode)
				{
					int stepx = currentx + xvalue[i];
					int stepy = currenty + yvalue[i];

					int pixelstepx = (stepy * 32) + (stepx * 32);
					int pixelstepy = (stepy * 16) - (stepx * 16);

					int pixeltargetx = (targetY * 32) + (targetX * 32);
					int pixeltargety = (targetY * 16) - (targetX * 16);

					int deltax = pixelstepx - pixeltargetx; if (deltax < 0) deltax *= -1;
					int deltay = pixelstepy - pixeltargety; if (deltay < 0) deltay *= -1;

					int h = deltax + deltay;
					int f = gvalue[i] + h;

					whichList[currentx+xvalue[i]][currenty+yvalue[i]] = 1;
					Node currentNode = {currentx+xvalue[i], currenty+yvalue[i], f, closedList.size()-1, i+1};
					openList.push_back(currentNode);
				}
            }
        }

        // 5. Find the lowest f in the open list, put it as current step
        int lowestf = 10000;
        for (int i=0; i<openList.size(); i++)
        {
            if (openList[i].f < lowestf)
            {
                lowestf = openList[i].f;
                currentx = openList[i].x;
                currenty = openList[i].y;
            }
        }
    }

    if (pathFound)
    {
        bool foundStart = false;

        // 1. Put the last item in path
        path.push_back(closedList[closedList.size()-1]);
        int nextpid = path[0].pid;
        if (nextpid < 1) foundStart = true;

        // 2. Look for pid of last item, put in path, untill back to starting point
        while (!foundStart)
        {
            path.push_back(closedList[nextpid]);
            nextpid = path.back().pid;

            if (nextpid < 1) {foundStart = true; break;}
        }

        // 3. Reverse the path

        vector<Node> zpath;
        for (int i=0; i<path.size(); i++)
        {
			//cout << "Step number " << i << " : dir " << path[path.size()-1-i].dir << " to " << path[path.size()-1-i].x << "," <<path[path.size()-1-i].y << endl;
            zpath.push_back(path[path.size()-1-i]);
        }
        path = zpath;

		return true;

    } else {

		return false;
	}
}

// Get the name of the map
string Maps::GetMapName()
{
    return m_mapName;
}

int Maps::GetTeleportationID(int x, int y)
{
	for (int i=0; i<m_numberOfTeleportation; i++)
	{
		if (m_vectTeleportation[0][i] == x && m_vectTeleportation[1][i] == y)
		{
			return i;
		}
	}

	return -1;
}

// Get the teleportation map
int Maps::GetTeleMap(int teleID)
{
    return m_vectTeleportation[2][teleID];
}

// Get the teleportation position (x)
int Maps::GetTeleX(int teleID)
{
    return m_vectTeleportation[3][teleID];
}

// Get the teleportation position (y)
int Maps::GetTeleY(int teleID)
{
    return m_vectTeleportation[4][teleID];
}
